﻿using Nop.Core;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.WindAlert;
using Nop.Plugin.Misc.WebServices.Core;
using Nop.Services.Authentication;
using Nop.Services.Authentication.External;
using Nop.Services.Catalog;
using Nop.Services.Customers;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.WindAlert;
using Nop.Web.Framework.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace Nop.Plugin.Misc.WebServices.Controllers
{
    public class ApiController : BaseController
    {
        #region Fields

        private readonly ICustomerService _customerService;
        private readonly ICustomerRegistrationService _customerRegistrationService;
        private readonly IWorkContext _workContext;
        private readonly IProductAttributeParser _productAttributeParser;
        private readonly WebServicesSettings _settings;
        private readonly CustomerSettings _customerSettings;
        private readonly IAuthenticationService _authenticationService;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly ILocalizationService _localizationService;
        private readonly IAndroidProviderFacebookAuthorizer _androidProviderFacebookAuthorizer;
        private readonly IMobileDeviceService _mobileDeviceService;

        #endregion

        #region Ctor

        public ApiController(
            ICustomerService customerService,
            ICustomerRegistrationService customerRegistrationService,
            IWorkContext workContext,
            IProductAttributeParser productAttributeParser,
            WebServicesSettings settings,
            CustomerSettings customerSettings,
            IAuthenticationService authenticationService,
            ICustomerActivityService customerActivityService,
            ILocalizationService localizationService,
            IAndroidProviderFacebookAuthorizer androidProviderFacebookAuthorizer,
            IMobileDeviceService mobileDeviceService)
        {
            _customerService = customerService;
            _customerRegistrationService = customerRegistrationService;
            _workContext = workContext;
            _productAttributeParser = productAttributeParser;
            _settings = settings;
            _customerSettings = customerSettings;
            _authenticationService = authenticationService;
            _customerActivityService = customerActivityService;
            _localizationService = localizationService;
            _androidProviderFacebookAuthorizer = androidProviderFacebookAuthorizer;
            _mobileDeviceService = mobileDeviceService;
        }

        #endregion

        #region Customers

        public ActionResult GetCustomerById(int id, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            var customer = _customerService.GetCustomerById(id);
            if (customer == null)
                return ErrorOccured("Customer not found.");

            return Successful(GetCustomerJson(customer));
        }

        public ActionResult GetCustomerByUsername(string username, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            var customer = _customerService.GetCustomerByUsername(username);
            if (customer == null)
                return ErrorOccured("Customer not found.");

            return Successful(GetCustomerJson(customer));
        }

        public ActionResult GetCustomerByEmail(string email, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            if (String.IsNullOrWhiteSpace(email))
                return ErrorOccured("Email is empty.");

            var customer = _customerService.GetCustomerByEmail(email);
            if (customer == null)
                return ErrorOccured("Customer not found.");

            return Successful(GetCustomerJson(customer));
        }

        public ActionResult GetCustomerByGuid(string guid, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            if (String.IsNullOrWhiteSpace(guid))
                return ErrorOccured("Guid is empty.");

            Guid id = new Guid(guid);

            var customer = _customerService.GetCustomerByGuid(id);
            if (customer == null)
                return ErrorOccured("Customer not found.");

            return Successful(GetCustomerJson(customer));
        }

        public ActionResult GetCustomerBySystemName(string systemName, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            if (String.IsNullOrWhiteSpace(systemName))
                return ErrorOccured("System Name is empty.");

            var customer = _customerService.GetCustomerBySystemName(systemName);
            if (customer == null)
                return ErrorOccured("Customer not found.");

            return Successful(GetCustomerJson(customer));
        }

        public ActionResult GetAllCustomers(string apiToken, DateTime? createdFromUtc = null,
            DateTime? createdToUtc = null, int affiliateId = 0, int vendorId = 0,
            string customerRoleIds = null, string email = null, string username = null,
            string firstName = null, string lastName = null,
            int dayOfBirth = 0, int monthOfBirth = 0,
            string company = null, string phone = null, string zipPostalCode = null,
            string loadOnlyWithShoppingCart = null, int shoppingCartTypeId = 0,
            int pageIndex = 0, int pageSize = 2147483647)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            int[] customerRole = null;
            ShoppingCartType? sct = null;
            bool isLoadOnlyWithShoppingCart = false;

            if (!String.IsNullOrWhiteSpace(customerRoleIds))
            {
                customerRole = Array.ConvertAll(customerRoleIds.Split(','), int.Parse);
            }

            if (shoppingCartTypeId > 0)
            {
                sct = (ShoppingCartType)shoppingCartTypeId;
            }

            if (!String.IsNullOrEmpty(loadOnlyWithShoppingCart))
            {
                if (loadOnlyWithShoppingCart.ToLower().Equals("true"))
                {
                    isLoadOnlyWithShoppingCart = true;
                }
            }

            var customers = _customerService.GetAllCustomers(createdFromUtc, createdToUtc, affiliateId, vendorId,
                customerRole, email, username, firstName, lastName, dayOfBirth, monthOfBirth, company, phone,
                zipPostalCode, isLoadOnlyWithShoppingCart, sct, pageIndex, pageSize);

            if (customers.Count == 0)
                return ErrorOccured("Customers are not found.");

            return Successful(GetCustomersJson(customers));
        }

        public ActionResult GetOnlineCustomers(string apiToken, string customerRoleIds = null,
             int pageIndex = 0, int pageSize = 2147483647)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            int[] customerRole = null;

            if (!String.IsNullOrWhiteSpace(customerRoleIds))
            {
                customerRole = Array.ConvertAll(customerRoleIds.Split(','), int.Parse);
            }

            var customers = _customerService.GetOnlineCustomers(DateTime.UtcNow.AddMinutes(-_customerSettings.OnlineCustomerMinutes), customerRole, pageIndex, pageSize);

            if (customers.Count == 0)
                return ErrorOccured("Customers are not found.");

            return Successful(GetCustomersJson(customers));
        }

        public ActionResult GetCustomersByIds(string apiToken, string customerRoleIds = null)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            int[] customerRole = null;

            if (!String.IsNullOrWhiteSpace(customerRoleIds))
            {
                customerRole = Array.ConvertAll(customerRoleIds.Split(','), int.Parse);
            }

            var customers = _customerService.GetCustomersByIds(customerRole);

            if (customers.Count == 0)
                return ErrorOccured("Customers are not found.");

            return Successful(GetCustomersJson(customers));
        }

        #endregion

        #region Misc

        public ActionResult InvalidApiToken(string apiToken)
        {
            var errorMessage = string.Empty;
            if (string.IsNullOrWhiteSpace(apiToken))
                errorMessage = "No API token supplied.";
            else
                errorMessage = string.Format("Invalid API token: {0}", apiToken);

            return ErrorOccured(errorMessage);
        }

        public ActionResult ErrorOccured(string errorMessage)
        {
            return Json(new
            {
                success = false,
                data = errorMessage
            }, JsonRequestBehavior.AllowGet);
        }

        public ActionResult Successful(object data)
        {
            return Json(new
            {
                success = true,
                data = data
            }, JsonRequestBehavior.AllowGet);
        }

        #endregion

        #region Helper methods

        private bool IsApiTokenValid(string apiToken)
        {
            if (string.IsNullOrWhiteSpace(apiToken) ||
                string.IsNullOrWhiteSpace(_settings.ApiToken))
                return false;

            return _settings.ApiToken.Trim().Equals(apiToken.Trim(),
                StringComparison.InvariantCultureIgnoreCase);
        }

        private object GetAddressJson(Address address)
        {
            if (address == null)
                return null;

            var addressJson = new
            {
                FirstName = address.FirstName,
                LastName = address.LastName,
                Email = address.Email,
                Company = address.Company,
                CountryId = address.CountryId,
                CountryName = address.Country == null ? null : address.Country.Name,
                StateProvinceId = address.StateProvinceId,
                StateProvinceName = address.StateProvince == null ? null : address.StateProvince.Name,
                City = address.City,
                Address1 = address.Address1,
                Address2 = address.Address2,
                ZipPostalCode = address.ZipPostalCode,
                PhoneNumber = address.PhoneNumber,
                FaxNumber = address.FaxNumber,
                CreatedOnUtc = address.CreatedOnUtc
            };

            return addressJson;
        }

        private object GetCustomerJson(Customer customer)
        {
            // TODO: refactor into own method for reuse
            var customerRoles = customer.CustomerRoles
                .Select(c =>
                    new
                    {
                        Id = c.Id,
                        Name = c.Name,
                        SystemName = c.SystemName
                    });

            // TODO: refactor into own method for reuse
            var externalAuthenticationRecords = customer.ExternalAuthenticationRecords
                .Select(e =>
                    new
                    {
                        Id = e.Id,
                        CustomerId = e.CustomerId,
                        Email = e.Email,
                        ExternalIdentifier = e.ExternalIdentifier,
                        ExternalDisplayIdentifier = e.ExternalDisplayIdentifier,
                        OAuthToken = e.OAuthToken,
                        OAuthAccessToken = e.OAuthAccessToken,
                        ProviderSystemName = e.ProviderSystemName
                    });

            // TODO: refactor into own method for reuse
            var shoppingCartItem = customer.ShoppingCartItems
                .Select(c =>
                    new
                    {
                        Id = c.Id,
                        StoreId = c.StoreId,
                        ShoppingCartTypeId = c.ShoppingCartTypeId,
                        CustomerId = c.CustomerId,
                        ProductId = c.ProductId,
                        AttributesXml = c.AttributesXml,
                        CustomerEnteredPrice = c.CustomerEnteredPrice,
                        Quantity = c.Quantity,
                        CreatedOnUtc = c.CreatedOnUtc,
                        UpdatedOnUtc = c.UpdatedOnUtc,
                        IsFreeShipping = c.IsFreeShipping,
                        IsShipEnabled = c.IsShipEnabled,
                        AdditionalShippingCharge = c.AdditionalShippingCharge,
                        IsTaxExempt = c.IsTaxExempt
                    });

            var customerJson = new
            {
                Id = customer.Id,
                CustomerGuid = customer.CustomerGuid,
                UserName = customer.Username,
                Email = customer.Email,
                CustomerRoles = customerRoles,
                AdminComment = customer.AdminComment,
                IsTaxExempt = customer.IsTaxExempt,
                AffiliateId = customer.AffiliateId,
                VendorId = customer.VendorId,
                HasShoppingCartItems = customer.HasShoppingCartItems,
                Active = customer.Active,
                Deleted = customer.Deleted,
                IsSystemAccount = customer.IsSystemAccount,
                SystemName = customer.SystemName,
                LastIpAddress = customer.LastIpAddress,
                CreatedOnUtc = customer.CreatedOnUtc,
                LastLoginDateUtc = customer.LastLoginDateUtc,
                LastActivityDateUtc = customer.LastActivityDateUtc,
                ExternalAuthenticationRecords = externalAuthenticationRecords,
                ShoppingCartItems = shoppingCartItem
            };

            return customerJson;
        }

        private object GetAppCustomerJson(Customer customer)
        {
            var customerJson = new
            {
                Id = customer.Id,
                CustomerGuid = customer.CustomerGuid,
                Fullname = customer.GetFullName(),
                UserName = customer.Username,
                Email = customer.Email,
                AdminComment = customer.AdminComment,
                IsTaxExempt = customer.IsTaxExempt,
                AffiliateId = customer.AffiliateId,
                VendorId = customer.VendorId,
                HasShoppingCartItems = customer.HasShoppingCartItems,
                Active = customer.Active,
                Deleted = customer.Deleted,
                IsSystemAccount = customer.IsSystemAccount,
                SystemName = customer.SystemName,
                LastIpAddress = customer.LastIpAddress,
                CreatedOnUtc = customer.CreatedOnUtc,
                LastLoginDateUtc = customer.LastLoginDateUtc,
                LastActivityDateUtc = customer.LastActivityDateUtc,
            };

            return customerJson;
        }

        // TODO: add in paging info
        private object GetCustomersJson(IList<Customer> customers)
        {
            var customerJsonList = new List<object>();

            foreach (var customer in customers)
            {
                customerJsonList.Add(GetCustomerJson(customer));
            }

            return customerJsonList;
        }

        #endregion

        #region App Customer login

        public ActionResult ApiLogIn(string email, string userName, string password, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);
            if (_customerSettings.UsernamesEnabled && userName != null)
            {
                userName = userName.Trim();
            }
            var loginResult = _customerRegistrationService.ValidateCustomer(_customerSettings.UsernamesEnabled ? userName : email, password);
            string errorMesssage = "Default error";
            switch (loginResult)
            {
                case CustomerLoginResults.Successful:
                    {
                        var customer = _customerSettings.UsernamesEnabled ? _customerService.GetCustomerByUsername(userName) : _customerService.GetCustomerByEmail(email);
                        return Successful(GetAppCustomerJson(customer));
                    }
                case CustomerLoginResults.CustomerNotExist:
                    errorMesssage = "Not exist"; //_localizationService.GetResource("Account.Login.WrongCredentials.CustomerNotExist");
                    break;
                case CustomerLoginResults.Deleted:
                    errorMesssage = "Deleted"; //_localizationService.GetResource("Account.Login.WrongCredentials.Deleted");
                    break;
                case CustomerLoginResults.NotActive:
                    errorMesssage = "Not Active";// _localizationService.GetResource("Account.Login.WrongCredentials.NotActive");
                    break;
                case CustomerLoginResults.NotRegistered:
                    errorMesssage = "Not Registered"; // _localizationService.GetResource("Account.Login.WrongCredentials.NotRegistered");
                    break;
                case CustomerLoginResults.WrongPassword:
                default:
                    errorMesssage = "Incurrect password"; // _localizationService.GetResource("Account.Login.WrongCredentials");
                    break;
            }
            return ErrorOccured(errorMesssage);
        }

        public ActionResult LoginInternal(string id, string email, string name, string gender, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            var result = _androidProviderFacebookAuthorizer.VerifyAuthentication(id,email, name, gender);
            switch (result.AuthenticationStatus)
            {
                case OpenAuthenticationStatus.Error:
                    {
                        return ErrorOccured(result.Errors[0]);
                    }
                case OpenAuthenticationStatus.AssociateOnLogon:
                    {
                        return ErrorOccured(OpenAuthenticationStatus.AssociateOnLogon.ToString());
                    }
                case OpenAuthenticationStatus.AutoRegisteredEmailValidation:
                    {
                        return ErrorOccured(OpenAuthenticationStatus.AutoRegisteredEmailValidation.ToString());
                    }
                case OpenAuthenticationStatus.AutoRegisteredAdminApproval:
                    {
                        return ErrorOccured(OpenAuthenticationStatus.AutoRegisteredAdminApproval.ToString());
                    }
                case OpenAuthenticationStatus.AutoRegisteredStandard:
                    {
                        var customer = _customerSettings.UsernamesEnabled ? _customerService.GetCustomerByUsername(email) : _customerService.GetCustomerByEmail(email);
                        return Successful(GetAppCustomerJson(customer));
                    }
                default:
                    break;
            }

            if (result.Result != null)
            {
                var customer = _customerSettings.UsernamesEnabled ? _customerService.GetCustomerByUsername(email) : _customerService.GetCustomerByEmail(email);
                return Successful(GetAppCustomerJson(customer));
            }
            return null;
        }
        
        #endregion


     

        #region Mobile device  

        private object GetAppMobileDeviceJson(MobileDevice mobileDevice, bool isNew)
        {
            var mobileDeviceJson = new
            {
                GCMRegistrationToken = mobileDevice.GCMRegistrationToken,
                CustomerId = mobileDevice.CustomerId,
                NewOrUpdate = isNew? "New Device added": "Device updated"
            };

            return mobileDeviceJson;
        }

        public ActionResult AddMobileDevice(int customerId, string gCMRegistrationToken, string deviceId, string phoneNumber, string deviceModel, 
                                            string operationSystem, string operationSystemVersion, string operationSystemId, string apiToken)
        {
            if (!IsApiTokenValid(apiToken))
                return InvalidApiToken(apiToken);

            if (customerId == 0 )
                return ErrorOccured("No customer passed");

            if (string.IsNullOrEmpty( gCMRegistrationToken))
                return ErrorOccured("No GCMRegistration passed");

            var currentDevice = _mobileDeviceService.GetMobileDeviceByGCMRegistrationToken(gCMRegistrationToken);

            if (currentDevice != null)
                if(currentDevice.CustomerId != customerId)
                    return ErrorOccured("GCMRegistration does not mach customer");

            if (currentDevice==null)
                currentDevice = _mobileDeviceService.GetMobileDevicesByCustomerIdBySystem(customerId, operationSystem, operationSystemId);

            if (currentDevice == null)
            {
                var mobileDevice = new MobileDevice()
                {
                    CustomerId = customerId,
                    GCMRegistrationToken = gCMRegistrationToken,
                    DeviceId = deviceId,
                    PhoneNumber = phoneNumber,
                    DeviceModel = deviceModel,
                    OperationSystem = operationSystem,
                    OperationSystemVersion = operationSystemVersion,
                    OperationSystemId = operationSystemId,
                    Active = true,
                };
                mobileDevice.CreatedOnUtc = DateTime.UtcNow;
                mobileDevice.UpdatedOnUtc = DateTime.UtcNow;
                _mobileDeviceService.InsertMobileDevice(mobileDevice);
                return Successful(GetAppMobileDeviceJson(mobileDevice, true));
            }
            else
            {
                currentDevice.GCMRegistrationToken = gCMRegistrationToken;
                currentDevice.PhoneNumber = phoneNumber;
                currentDevice.DeviceModel = deviceModel;
                currentDevice.OperationSystem = operationSystem;
                currentDevice.OperationSystemVersion = operationSystemVersion;
                currentDevice.OperationSystemId = operationSystemId;
                currentDevice.UpdatedOnUtc = DateTime.UtcNow;
                _mobileDeviceService.UpdateMobileDevice(currentDevice);
                return Successful(GetAppMobileDeviceJson(currentDevice, false));
            }
            
        }

        #endregion
    }
}